package org.uim.cricket.analyzer.analyze.report;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import org.uim.cricket.analyzer.analyze.Alogger;
import org.uim.cricket.analyzer.report.XMLInningReporting;
import org.uim.cricket.analyzer.report.XMLMatchHeaderReporting;
import org.uim.cricket.entities.MatchPlayer;
import org.uim.cricket.entities.Player;
import org.uim.cricket.entities.aggr.SingleInning;
import org.uim.cricket.entities.batting.BattingInning;
import org.uim.cricket.entities.bowling.BowlingInning;
import org.uim.cricket.entities.fielding.SingleFielding;
import org.uim.cricket.entities.match.CricketMatch;
import org.uim.cricket.entities.partnerships.Partnership;
import org.uim.cricket.entities.persist.impl.XMLBatsmanOutDetailPersist;
import org.uim.cricket.entities.persist.impl.XMLBatsmanStatisticsPersist;
import org.uim.cricket.entities.persist.impl.XMLBowlerStatisticsPersist;
import org.uim.cricket.entities.persist.impl.XMLSingleFieldingPersist;
import org.uim.cricket.util.io.FileSaver;

public class XMLAnalyzerReporter implements AnalyzerReportable {
	
	private StringBuilder xml = null;
	private String xmlFile = null;
	private boolean insertMatchHeader = true;
	private boolean insertInningSummary = true;
	private boolean insertBattings = false;
	private boolean insertBowlings = false;
	private boolean insertFallOfWickets = true;
	
	private XMLBatsmanOutDetailPersist xmlBatOutDetials = new XMLBatsmanOutDetailPersist();
	private XMLBatsmanStatisticsPersist xmlBatStats = new XMLBatsmanStatisticsPersist();
	private XMLBowlerStatisticsPersist xmlBowlStats = new XMLBowlerStatisticsPersist();
	private XMLSingleFieldingPersist xmlFieldStats = new XMLSingleFieldingPersist();
	
	public XMLAnalyzerReporter() {
		
	}
	
	public XMLAnalyzerReporter(String theXMLFile) {
		this.xmlFile = theXMLFile;
	}
	
	@Override
	public void report(CricketMatch match, Map<String, List<MatchPlayer>> players) throws Exception {
		if (match == null || players == null) return;
		
		xml = new StringBuilder();
		xml.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
		xml.append("<AnalyzeReport>\n");
		
		if (this.isInsertMatchHeader()) {
			XMLMatchHeaderReporting hreport = new XMLMatchHeaderReporting();
			xml.append(hreport.getMatchHeaderXMLText(match).toString());
	        xml.append("</CricketMatch>\n");
			xml.append("\n");
		}
		if (this.isInsertInningSummary() && match.getInnings() != null) {
			XMLInningReporting innReport = new XMLInningReporting(insertBattings, insertBowlings, insertFallOfWickets);
			SingleInning[] inns = match.getInnings();
			for (int i=0; i<inns.length; i++) {
				if (inns[i] == null) continue;

				StringBuilder res = innReport.appendSingleInning(inns[i], 0);
				xml.append(res.toString());
			}
			
		}
		
		for (String key : players.keySet()) {
			
			xml.append("<Team name=\"").append(key).append("\">\n");
			
			List<MatchPlayer> ps = players.get(key);
			if (ps != null) {
				
				for (int i=0;i<ps.size();i++) {
					if (ps.get(i)==null) continue;
					
					this.appendMatchPlayer(ps.get(i), match); 
				}
				
			}
			
			xml.append("</Team>\n");
		}
		
		SingleInning[] inns = match.getInnings();
		if (inns != null) {
			
			xml.append("<PartnershipSet>\n");
			
			for (int i=0;i<inns.length;i++) {
				SingleInning inn = inns[i];
				if (inn == null || inn.getPartnerships() == null) continue;
				
				this.indent(1);
				xml.append("<PartnershipInn inning=\"").append(inn.getInningMatch()).append("\"");
				xml.append(" team=\"").append(inn.getBattedTeam()).append("\">\n");
				
				for (int p=0;p<inn.getPartnerships().length;p++) {
					Partnership pship = inn.getPartnerships()[p];
					
					this.insertPShip(pship, match);
				}
				
				this.indent(1);
				xml.append("</PartnershipInn>\n");
				
			}
			
			xml.append("</PartnershipSet>\n");
			
		}
		
		xml.append("</AnalyzeReport>");
		
		if (this.xmlFile != null) {
            Alogger.debug("  Saving to file: " + this.xmlFile);
            FileSaver fs = new FileSaver(this.xmlFile);
            
            try {
                fs.write(xml);
            } catch (IOException e) {
                Alogger.debug("Cann't write to the file " + this.xmlFile);
                Alogger.debug("", e);
            }
            
        } else {
        	Alogger.debug(" No reporting file is defined!");
        	Alogger.debug(" Reporting will not be operated!");
        }
	}

	private void appendMatchPlayer(MatchPlayer player, CricketMatch match) {
		Player p = player.getPlayer();
		if (p==null) return;
		
		this.indent(1);
		xml.append("<Player id=\"").append(p.getId()).append("\"");
		if (p.getPlayerLink() != null) {
			xml.append(" link=\"").append(p.getPlayerLink()).append("\"");
		}
		xml.append(" name=\"").append(p.getTitleName()).append("\"");
		xml.append(" shortname=\"").append(p.getShortName()).append("\"");
		if (p.isCaptain()) xml.append(" captain=\"true\"");
		if (p.isWicketKeeper()) xml.append(" keeper=\"true\"");
		xml.append(">\n");

		if (player.getBattingInnings() != null) {
			List<BattingInning> batinns = player.getBattingInnings();
			for (int i=0;i<batinns.size();i++) {
				this.insertBatInn(batinns.get(i), match);
			}
		}
		if (player.getBowlingInnings() != null) {
			List<BowlingInning> bowlinns = player.getBowlingInnings();
			for (int i=0;i<bowlinns.size();i++) {
				this.insertBowlInn(bowlinns.get(i), match);
			}
		}
		if (player.getFieldingEfforts() != null) {
			this.indent(2);
			xml.append("<FieldingEfforts>\n");
			List<SingleFielding> fields = player.getFieldingEfforts();
			for (int i=0;i<fields.size();i++) {
				if (fields.get(i)==null) continue;
				
				this.indent(3);
				xml.append(this.xmlFieldStats.doPersist(fields.get(i)));
				xml.append("\n");
			}
			
			this.indent(2);
			xml.append("</FieldingEfforts>\n");
		}
		
		this.indent(1);
		xml.append("</Player>\n");
		
	}
	
	private void insertBatInn(BattingInning batinn, CricketMatch match) {
		if (batinn == null) return;
		
		this.indent(2);
		xml.append("<Batting inning=\"").append(batinn.getBattedInning()).append("\"");
		xml.append(" position=\"").append(batinn.getBattedPosition()).append("\">\n");
		
		if (batinn.getOutDetails() != null) {
			this.indent(3);
			xml.append(this.xmlBatOutDetials.doPersist(batinn.getOutDetails()));
			xml.append("\n");
		}
		
		if (batinn.getStatistics() != null) {
			this.indent(3);
			xml.append(this.xmlBatStats.doPersist(batinn.getStatistics()));
			xml.append("\n");
		}
		
		this.indent(2);
		xml.append("</Batting>\n");
	}
	
	private void insertBowlInn(BowlingInning bowlinn, CricketMatch match) {
		if (bowlinn == null) return;
		
		this.indent(2);
		xml.append("<Bowling inning=\"").append(bowlinn.getBowlingInning()).append("\"");
		xml.append(" position=\"").append(bowlinn.getBowlingPosition()).append("\">\n");
		
		if (bowlinn.getStatistics() != null) {
			this.indent(3);
			xml.append(this.xmlBowlStats.doPersist(bowlinn.getStatistics()));
			xml.append("\n");
			
			if (bowlinn.getOutBatsmen() != null) {
				this.indent(3);
				xml.append("<WicketsTaken>\n");
				
				bowlinn.sortWickets();
				List<BattingInning> batsmen = bowlinn.getOutBatsmen();
				
				for (int i=0;i<batsmen.size();i++) {
					if (batsmen.get(i)==null) continue;
					
					BattingInning bi = batsmen.get(i);
					if (bi.getBatsman() != null) {
						this.indent(5);
						xml.append("<Victim batterID=\"").append(bi.getBatsman().getId()).append("\"");
						if (bi.getOutDetails() != null && bi.getOutDetails().getFallofWicket() != null) {
							if (bi.getOutDetails().getFallofWicket().getFallOfOver() != null) {
								xml.append(" fallInOver=\"").append(bi.getOutDetails().getFallofWicket().getFallOfOver()).append("\"");
							}
						}
						xml.append(" />\n");
					}
				}
				
				this.indent(3);
				xml.append("</WicketsTaken>\n");
			}
			
			
		}
		
		this.indent(2);
		xml.append("</Bowling>\n");
	}
	
	private void insertPShip(Partnership pship, CricketMatch match) {
		if (pship == null) return;
		
		this.indent(2);
		xml.append("<Partnership").append(" wicket=\"").append(pship.getWicketNo()).append("\"");
		xml.append(" runs=\"").append(pship.getRuns()).append("\"");
		if (pship.getBalls() >= 0) {
			xml.append(" balls=\"").append(pship.getBalls()).append("\"");
		}
		if (pship.getStartPlayer() != null) {
			xml.append(" started=\"").append(pship.getStartPlayer().getBatsman().getId()).append("\"");
		}
		if (pship.getArrivedPlayer() != null) {
			xml.append(" arrived=\"").append(pship.getArrivedPlayer().getBatsman().getId()).append("\"");
		}
		if (pship.getEndWicket() != null) {
			xml.append(" out=\"").append(pship.getEndWicket().getOutPlayer().getId()).append("\"");
			xml.append(" outAt=\"").append(pship.getEndWicket().getFallOfRuns()).append("\"");
			if (pship.getEndWicket().isRetiredHurt()) {
				xml.append(" retired=\"true\"");
			}
		} else {
			xml.append(" notout=\"true\"");
		}
		xml.append(" />\n");

	}
	
	private void indent(int size) {
		for (int i=0;i<size;i++) xml.append("\t");
	}
	
	public String getXmlFile() {
		return xmlFile;
	}
	
	public void setXmlFile(String xmlFile) {
		this.xmlFile = xmlFile;
	}
	
	public boolean isInsertMatchHeader() {
		return insertMatchHeader;
	}
	
	public void setInsertMatchHeader(boolean insertMatchHeader) {
		this.insertMatchHeader = insertMatchHeader;
	}
	
	public void setInsertInningSummary(boolean insertInningSummary) {
		this.insertInningSummary = insertInningSummary;
	}
	
	public void setInsertFallOfWickets(boolean insertFallOfWickets) {
		this.insertFallOfWickets = insertFallOfWickets;
	}
	
	public void setInsertBowlings(boolean insertBowlings) {
		this.insertBowlings = insertBowlings;
	}
	
	public void setInsertBattings(boolean insertBattings) {
		this.insertBattings = insertBattings;
	}
	
	public boolean isInsertInningSummary() {
		return insertInningSummary;
	}
	
	public boolean isInsertFallOfWickets() {
		return insertFallOfWickets;
	}
	
	public boolean isInsertBowlings() {
		return insertBowlings;
	}
	
	public boolean isInsertBattings() {
		return insertBattings;
	}
	
}
